﻿using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace WebApplication1.CacheHelper
{
    public class DistributedCachedHelper : IDistributedCacheHelper
    {
        private readonly IDistributedCache _distributedCache;

        public DistributedCachedHelper(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }

        /// <summary>
        /// 该方法用于设置缓存项的随机过期时间。它接受一个基础过期时间 baseExpireSeconds，然后在基础过期时间的范围内生成一个随机的过期时间，并将其设置为缓存项的绝对过期时间。
        /// </summary>
        /// <param name="baseExpireSeconds"></param>
        /// <returns></returns>
        private static DistributedCacheEntryOptions CreateOptions(int baseExpireSeconds)
        {
            double sec = Random.Shared.Next(baseExpireSeconds, baseExpireSeconds * 2);
            TimeSpan expirationTime = TimeSpan.FromSeconds(sec);
            return new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = expirationTime
            };
        }

        public TResult? GetOrCreate<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, TResult?> valueFactory, int expireSeconds = 60)
        {
            string jsonStr = _distributedCache.GetString(cacheKey);
            if (string.IsNullOrEmpty(jsonStr))
            {
                var options = CreateOptions(expireSeconds);
                TResult? result = valueFactory(options);
                string jsonOfResult = JsonSerializer.Serialize(result, typeof(TResult));
                _distributedCache.SetString(cacheKey, jsonOfResult, options);
                return result;
            }
            else
            {
                _distributedCache.Refresh(cacheKey);
                return JsonSerializer.Deserialize<TResult>(jsonStr);
            }
        }

        public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds = 60)
        {
            string jsonStr = await _distributedCache.GetStringAsync(cacheKey);
            if (string.IsNullOrEmpty(jsonStr))
            {
                var options = CreateOptions(expireSeconds);
                TResult? result = await valueFactory(options);
                string jsonOfResult = JsonSerializer.Serialize(result,typeof(TResult));
                await _distributedCache.SetStringAsync(cacheKey, jsonOfResult, options);
                return result;
            }
            else
            {
                await _distributedCache.RefreshAsync(cacheKey);
                return JsonSerializer.Deserialize<TResult>(jsonStr);
            }
        }

        public void Remove(string cacheKey)
        {
            _distributedCache.Remove(cacheKey);
        }

        public async Task RemoveAsync(string cacheKey)
        {
            await _distributedCache.RemoveAsync(cacheKey);
        }
    }
}
